﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

/*
	todo:
	save
	validate
	insert
		- move others
		- clear others
	delete
	
*/

namespace SqlCeDriver
{
	public partial class CreateTableForm : Form
	{
		const int MAX_INDEX = 9;
		enum FormControl {insert, delete, column, type, len, pk}
		private static readonly Dictionary<FormControl, String> NameLookup = new Dictionary<FormControl,string>
		{
			{FormControl.insert, "linkIns"},
			{FormControl.delete, "linkDel"},
			{FormControl.column, "textColName"},
			{FormControl.type, "comboType"},
			{FormControl.len, "textLength"},
			{FormControl.pk, "comboPk"},
		};
		
		public String Script {get; set;}
		
		public CreateTableForm()
		{
			InitializeComponent();
			lblInfo.Text = INFO_TEXT;
			AddTypes();
			AddPkOptions();
			AddEvents();
			SetControlState();
		}

		private void AddEvents()
		{
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				TextBox tb = GetControl(idx, FormControl.column) as TextBox;
				tb.TextChanged += new System.EventHandler(this.TextColName_TextChanged);
				LinkLabel ll = GetControl(idx, FormControl.insert) as LinkLabel;
				ll.Click += new EventHandler(Insert_Click);
				ll = GetControl(idx, FormControl.delete) as LinkLabel;
				ll.Click += new EventHandler(Delete_Click);
				ComboBox cb = GetControl(idx, FormControl.type) as ComboBox;
				cb.SelectedIndexChanged += new EventHandler(Type_SelectedIndexChanged);
			}
		}

		private void AddPkOptions()
		{
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				ComboBox cb = GetControl(idx, FormControl.pk) as ComboBox;
				cb.Items.Add("Allow Nulls");
				cb.Items.Add("No Nulls");
				cb.Items.Add("PKey");
				cb.Items.Add("Auto PKey");
			}
		}

		private void AddTypes()
		{
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				ComboBox cb = GetControl(idx, FormControl.type) as ComboBox;
				cb.Items.Add("int");
				cb.Items.Add("nchar");
				cb.Items.Add("nvarchar");
				cb.Items.Add("datetime");
				cb.Items.Add("money");
			}
		}

		Control GetControl(int rowIndex, FormControl control)
		{
			String name = NameLookup[control] + rowIndex.ToString();
			return this.Controls[name];
		}

		void SetControlState()
		{
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				if (textTableName.Text.Length == 0)
					EnableRow(idx, false);
				else
				{
					if (idx == 0)
						EnableRow(idx, true);
				}
			}
		}

		private void EnableRow(int idx, bool enable)
		{
			foreach (FormControl ctl in Enum.GetValues(typeof(FormControl)))
			{
				GetControl(idx, ctl).Enabled = enable;
				if (enable == true && ctl == FormControl.len)
					if (!WantLengthEnabled(idx))
						GetControl(idx, ctl).Enabled = false;
			}
		}

		private bool WantLengthEnabled(int idx)
		{
			ComboBox cb = GetControl(idx, FormControl.type) as ComboBox;
			if (cb.SelectedIndex == -1) return false;
			String type = cb.Items[cb.SelectedIndex].ToString();
			if (type.ToUpper().IndexOf("CHAR") != -1)
				return true;
			return false;
		}
		
		private void BtnClose_Click(object sender, EventArgs e)
		{
			if (ValidatesOk())
			{
				GenerateScript();
				this.DialogResult = DialogResult.OK;
				this.Close();
			}
		}
		private void BtnPreview_Click(object sender, EventArgs e)
		{
			if (ValidatesOk())
			{
				GenerateScript();
				Clipboard.SetText(Script);
				MessageBox.Show(Script);
			}
		}

		private void GenerateScript()
		{
			String template = "create table "+Helper.SafeTableName(this.textTableName.Text) + " (\n{0}{1})";
			String fields = "";
			String pkey = "";
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				TextBox tb = GetControl(idx, FormControl.column) as TextBox;
				ComboBox cb = GetControl(idx, FormControl.type) as ComboBox;
				if (tb.Text.Length > 0 && cb.SelectedIndex != -1)
				{
					if (idx > 0) fields += ",\n";
					String column = Helper.SafeSqlField(tb.Text);
					fields += column;
					String type = cb.Items[cb.SelectedIndex].ToString();
					fields += " "+type;
					if (WantLengthEnabled(idx))
					{
						tb = GetControl(idx, FormControl.len) as TextBox;
						fields += "("+tb.Text+")";
					}
					cb = GetControl(idx, FormControl.pk) as ComboBox;
					String pkeyOpt = cb.Items[cb.SelectedIndex].ToString().ToUpper();
					if (pkeyOpt.IndexOf("AUTO") != -1)
					{
						fields += " identity(1,1) primary key";
					}
					else if (pkeyOpt.IndexOf("PK") != -1)
					{
						if (pkey.Length > 0) pkey += ",";
						pkey += column;
					}
				}
				else
					break;
			}
			if (pkey.Length > 0)
			{
				pkey = String.Format(",\nconstraint PK_"+this.textTableName.Text+" primary key ({0})", pkey);
			}
			Script = String.Format(template, fields, pkey);
		}

		private bool ValidatesOk()
		{	
			// must have entered a table name
			if (this.textTableName.Text.Trim().Length == 0)
				return ValidateError(-1, "Enter a table name then some fields.", this.textTableName);
			bool emptyLine = false;
			bool foundFilledLine = false;
			for (int idx = 0; idx <= MAX_INDEX; ++idx)
			{
				TextBox tb = GetControl(idx, FormControl.column) as TextBox;
				ComboBox cb = GetControl(idx, FormControl.type) as ComboBox;
				if (tb.Text.Length > 0 && cb.SelectedIndex != -1)
				{
					if (emptyLine == true) return ValidateError(idx - 1, "Use delete to remove blank rows before field definitions", GetControl(idx-1,FormControl.column));
					String column = tb.Text;
					String type = cb.Items[cb.SelectedIndex].ToString();
					tb = GetControl(idx, FormControl.len) as TextBox;
					if (WantLengthEnabled(idx))
					{
						int len = 0;
						bool parseOk = int.TryParse(tb.Text, out len);
						if (parseOk == false || len <= 0) return ValidateError(idx, "Invalid length entered.", tb);
					}
					cb = GetControl(idx, FormControl.pk) as ComboBox;
					String pkeyOpt = cb.Items[cb.SelectedIndex].ToString().ToUpper();
					if (pkeyOpt.ToUpper().IndexOf("AUTO") != -1)
						if (type.ToUpper().IndexOf("INT") == -1)
							return ValidateError(idx, "You can only define an auto-pk on an integer column.", cb);
					foundFilledLine = true;
				}
				else
					emptyLine = true;
			}
			// No fields were entered.
			if (foundFilledLine == false) return ValidateError(0, "Enter at least one field.", GetControl(0, FormControl.column));
			// Can't find anything wrong?
			return true;
		}

		private bool ValidateError(int rowIndex, string msg, Control errorControl)
		{
			MessageBox.Show(String.Format("Line {0}\n{1}", rowIndex + 1, msg), 
						"Form Input Error", 
						MessageBoxButtons.OK, MessageBoxIcon.Error);
			errorControl.Focus();
			return false;
		}

		private void BtnCancel_Click(object sender, EventArgs e)
		{
			this.DialogResult = DialogResult.Cancel;
			this.Close();
		}

		private void TextColName_TextChanged(object sender, EventArgs e)
		{
			TextBox tb = sender as TextBox;
			int rowIndex = IndexFromControl(tb);
			if (rowIndex < MAX_INDEX)
				EnableRow(rowIndex + 1, true);
			
			ComboBox cb = GetControl(rowIndex, FormControl.pk) as ComboBox;
			if (cb.SelectedIndex == -1)
				cb.SelectedIndex = 0;
			cb = GetControl(rowIndex, FormControl.type) as ComboBox;
			if (cb.SelectedIndex == -1)
				cb.SelectedIndex = 0;
		}

		private void TextTableName_TextChanged(object sender, EventArgs e)
		{
			SetControlState();
		}
		
		private void Type_SelectedIndexChanged(object sender, EventArgs e)
		{
			ComboBox cb = sender as ComboBox;
			int rowIndex = IndexFromControl(cb);
			TextBox tb = GetControl(rowIndex, FormControl.len) as TextBox;
			tb.Enabled = WantLengthEnabled(rowIndex);
		}

		private void Insert_Click(object sender, EventArgs e)
		{
			LinkLabel ll = sender as LinkLabel;
			int rowIndex = IndexFromControl(ll);
			MoveFrom(rowIndex, 1);
		}

		private void Delete_Click(object sender, EventArgs e)
		{
			LinkLabel ll = sender as LinkLabel;
			int rowIndex = IndexFromControl(ll);
			MoveFrom(rowIndex, 0);
		}

		private void MoveFrom(int startIndex, int moveUpOrDown)
		{
			if (moveUpOrDown == 1)
			{
				for (int idx = MAX_INDEX; idx > startIndex; --idx)
				{
					MoveOneControl(FormControl.column, idx - 1, idx);
					MoveOneControl(FormControl.type, idx - 1, idx);
					MoveOneControl(FormControl.len, idx - 1, idx);
					MoveOneControl(FormControl.pk, idx - 1, idx);
				}
				ClearRow(startIndex);
			}
			else
			{
				for (int idx = startIndex; idx < MAX_INDEX; ++idx)
				{
					MoveOneControl(FormControl.column, idx + 1, idx);
					MoveOneControl(FormControl.type, idx + 1, idx);
					MoveOneControl(FormControl.len, idx + 1, idx);
					MoveOneControl(FormControl.pk, idx + 1, idx);
				}
				ClearRow(MAX_INDEX);
			}
		}

		private void ClearRow(int rowIndex)
		{
			TextBox tb = GetControl(rowIndex, FormControl.column) as TextBox;
			tb.Text = "";
			tb = GetControl(rowIndex, FormControl.len) as TextBox;
			tb.Text = "";
			ComboBox cb = GetControl(rowIndex, FormControl.type) as ComboBox;
			cb.SelectedIndex = -1;
			cb = GetControl(rowIndex, FormControl.pk) as ComboBox;
			cb.SelectedIndex = -1;
		}

		private void MoveOneControl(FormControl ctl, int src, int tgt)
		{
			Control srcControl = GetControl(src, ctl);
			Control tgtControl = GetControl(tgt, ctl);
			if (srcControl.GetType() == typeof(TextBox))
				tgtControl.Text = srcControl.Text;
			if (srcControl.GetType() == typeof(ComboBox))
				(tgtControl as ComboBox).SelectedIndex = (srcControl as ComboBox).SelectedIndex;
		}

		private int IndexFromControl(Control control)
		{
			String name = control.Name;
			int index = 0;
			int.TryParse(name[name.Length-1].ToString(), out index);
			return index;
		}
		const String INFO_TEXT=
@"Use this form to create simple tables with up to 10 fields. You only need to add length for 
nchar or nvarchar types. Choose Auto PKey to create an auto increment integer primary
key. Press preview to see what script is generated. This is copied to the clipboard so you 
can make adjustments and run it elsewhere.
";
	}
}
